package com.bball.court.controller.parse;

import java.io.ByteArrayOutputStream;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.AsyncTask;

import com.bball.court.controller.CourtController;
import com.bball.court.http.AddPlayerToCourtRequestCallbacks;
import com.bball.court.http.DeletePlayerToCourtRequestCallbacks;
import com.bball.court.http.GetCourtsRequestCallbacks;
import com.bball.court.model.Court;
import com.bball.court.model.parse.ParseCourt;
import com.bball.court.model.parse.ParseCourtList;
import com.bball.court.util.BitmapUtil;
import com.parse.FindCallback;
import com.parse.GetCallback;
import com.parse.ParseException;
import com.parse.ParseGeoPoint;
import com.parse.ParseQuery;
import com.parse.ParseRelation;
import com.parse.ParseUser;
import com.parse.SaveCallback;

public class ParseCourtController implements CourtController {

    protected static final int RESULT_LIMIT = 30;
    protected static final String LOCATION = "location";
    private ParseQuery<ParseCourt> query;
    private Context context;

    public ParseCourtController(Context context) {
        this.context = context;
    }

    @Override
    public void findClosestCourt(GetCourtsRequestCallbacks callback, double latitude, double longitude) {
        findClosestCourt(callback, latitude, longitude, 10);
    }

    @Override
    public void findClosestCourt(final GetCourtsRequestCallbacks callback, final double latitude, final double longitude, int distanceInKm) {
        ParseGeoPoint userLocation = new ParseGeoPoint(latitude, longitude);
        query = getQuery().whereWithinKilometers(LOCATION, userLocation, distanceInKm);
        query.include("post");
        query.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE);
        query.setMaxCacheAge(1000 * 60 * 5);
        query.findInBackground(new FindCallback<ParseCourt>() {
            @Override
            public void done(List<ParseCourt> courts, ParseException exception) {
                if (exception == null) {
                    new PersistCourtTask(context).execute(courts.toArray(new Court[courts.size()]));
                    if (callback != null) {
                        callback.onGetCourtsRequestComplete(new ParseCourtList(courts));
                    }
                } else {
                    callback.onGetCourtsRequestFailed(new RuntimeException(exception));
                }
            }
        });
    }

    private ParseQuery<ParseCourt> getQuery() {
        query = ParseQuery.getQuery(ParseCourt.class);
        query.setLimit(RESULT_LIMIT);
        return query;
    }

    @Override
    public void canceltasks() {
        if (query != null) {
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    query.cancel();
                    return null;
                }
            };
        }
    }

    @Override
    public void addSuggestedCourt(String name, String fileUri, double[] latLong, String[] address, boolean isFree, boolean isOutdoor) {
        ParseCourt parseCourt = new ParseCourt();
        parseCourt.setAddress(address[0]);
        parseCourt.setCity(address[1]);
        parseCourt.setCountry(address[2]);
        parseCourt.setGeolocation(latLong[0], latLong[1]);
        parseCourt.setPicture(getBytes(fileUri));
        parseCourt.setName(name);
        parseCourt.setFree(isFree);
        parseCourt.setOutdoor(isOutdoor);
        parseCourt.setSuggestedBy(ParseUser.getCurrentUser());
        parseCourt.saveWithPicture();
    }

    private byte[] getBytes(String fileUri) {
        Bitmap resizedBitmap = BitmapUtil.getResizedBitmap(Uri.parse(fileUri));
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        resizedBitmap.compress(CompressFormat.PNG, 0, stream);
        return stream.toByteArray();
    }

    @Override
    public void addPlayer(String courtId, final AddPlayerToCourtRequestCallbacks addPlayerToCourtRequestCallbacks) {
        final ParseUser parseUser = ParseUser.getCurrentUser();
        if (parseUser == null) {
            addPlayerToCourtRequestCallbacks.onAddPlayerFail(new RuntimeException("User must be connected"));
            return;
        }

        ParseQuery<ParseCourt> query = ParseQuery.getQuery(ParseCourt.class);
        query.getInBackground(courtId, new GetCallback<ParseCourt>() {

            @Override
            public void done(final ParseCourt court, ParseException e) {
                if (e == null) {
                    ParseRelation<ParseUser> relation = court.getPlayersRelation();
                    relation.add(parseUser);
                    court.saveInBackground(new SaveCallback() {

                        @Override
                        public void done(ParseException e) {
                            if (e == null) {
                                addPlayerToCourtRequestCallbacks.onAddPlayerSuccess(court);
                            } else {
                                addPlayerToCourtRequestCallbacks.onAddPlayerFail(new RuntimeException(e));
                            }
                        }
                    });
                } else {
                    addPlayerToCourtRequestCallbacks.onAddPlayerFail(new RuntimeException(e));
                }
            }
        });
    }

    @Override
    public void removePlayer(String courtId, final DeletePlayerToCourtRequestCallbacks deletePlayerCallback) {
        final ParseUser parseUser = ParseUser.getCurrentUser();
        if (parseUser == null) {
            deletePlayerCallback.onDeletePlayerFail(new RuntimeException("User must be connected"));
            return;
        }
        ParseQuery<ParseCourt> query = ParseQuery.getQuery(ParseCourt.class);
        query.getInBackground(courtId, new GetCallback<ParseCourt>() {

            @Override
            public void done(final ParseCourt court, ParseException e) {
                if (e == null) {
                    ParseRelation<ParseUser> relation = court.getRelation("currentPlayers");
                    relation.remove(parseUser);
                    court.saveInBackground(new SaveCallback() {

                        @Override
                        public void done(ParseException e) {
                            if (e == null) {
                                deletePlayerCallback.onDeletePlayerSuccess(court);
                            } else {
                                deletePlayerCallback.onDeletePlayerFail(new RuntimeException(e));
                            }
                        }
                    });
                } else {
                    deletePlayerCallback.onDeletePlayerFail(new RuntimeException(e));
                }
            }
        });
    }
}
